﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections;
using BOMS.Utility.DATA;
using BOMSConsoleService.Lib;
using BOMS.DATA;
using BOMS.Lib;
using System.Configuration;
namespace IOCPServer
{

    // Implements the connection logic for the socket server.  
    // After accepting a connection, all data read from the client 
    // is sent back to the client. The read and echo back to the client pattern 
    // is continued until the client disconnects.
    class Server
    {
        static readonly bool ShowConsoleDataScreen = bool.Parse(ConfigurationManager.AppSettings["ShowConsoleDataScreen"]);
        #region 原始代码
        private int m_maxConnections;   // the maxoimum number of connections the sample is designed to handle simultaneously 
        private int m_receiveBufferSize;// buffer size to use for each socket I/O operation 
        BufferManager bufferManager;  // represents a large reusable set of buffers for all socket operations
        const int opsToPreAlloc = 2;    // read, write (don't alloc buffer space for accepts)
        Socket listenSocket;            // the socket used to listen for incoming connection requests
        // pool of reusable SocketAsyncEventArgs objects for write, read and accept socket operations
        SocketAsyncEventArgsPool m_readWritePool;
        int m_totalBytesRead;           // counter of the total # bytes received by the server
        int m_numConnectedSockets;      // the total number of clients connected to the server 
        Semaphore m_maxNumberAcceptedClients;

        // Create an uninitialized server instance.  
        // To start the server listening for connection requests
        // call the Init method followed by Start method 
        //
        // <param name="numConnections">the maximum number of connections the sample is designed to handle simultaneously</param>
        // <param name="receiveBufferSize">buffer size to use for each socket I/O operation</param>
        public Server(int numConnections, int receiveBufferSize)
        {
            m_totalBytesRead = 0;
            m_numConnectedSockets = 0;
            m_maxConnections = numConnections;
            m_receiveBufferSize = receiveBufferSize;
            // allocate buffers such that the maximum number of sockets can have one outstanding read and 
            //write posted to the socket simultaneously  
            bufferManager = new BufferManager(receiveBufferSize * numConnections * opsToPreAlloc,
                receiveBufferSize);

            m_readWritePool = new SocketAsyncEventArgsPool(numConnections);
            m_maxNumberAcceptedClients = new Semaphore(numConnections, numConnections);
        }

        // Initializes the server by preallocating reusable buffers and 
        // context objects.  These objects do not need to be preallocated 
        // or reused, but it is done this way to illustrate how the API can 
        // easily be used to create reusable objects to increase server performance.
        //
        public void Init()
        {
            // Allocates one large byte buffer which all I/O operations use a piece of.  This gaurds 
            // against memory fragmentation
            bufferManager.InitBuffer();

            // preallocate pool of SocketAsyncEventArgs objects
            SocketAsyncEventArgs readWriteEventArg;

            for (int i = 0; i < m_maxConnections; i++)
            {
                //Pre-allocate a set of reusable SocketAsyncEventArgs
                readWriteEventArg = new SocketAsyncEventArgs();
                readWriteEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
                readWriteEventArg.UserToken = new AsyncUserToken();

                // assign a byte buffer from the buffer pool to the SocketAsyncEventArg object
                bufferManager.SetBuffer(readWriteEventArg);

                // add SocketAsyncEventArg to the pool
                m_readWritePool.Push(readWriteEventArg);
            }

        }

        // Starts the server such that it is listening for 
        // incoming connection requests.    
        //
        // <param name="localEndPoint">The endpoint which the server will listening 
        // for connection requests on</param>
        public void Start(IPEndPoint localEndPoint)
        {
            // create the socket which listens for incoming connections
            listenSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            listenSocket.Bind(localEndPoint);
            // start the server with a listen backlog of 100 connections
            listenSocket.Listen(200);

            // post accepts on the listening socket
            StartAccept(null);

            //Console.WriteLine("{0} connected sockets with one outstanding receive posted to each....press any key", m_outstandingReadCount);
            // Console.WriteLine("Press any key to terminate the server process....");
            // Console.ReadKey();
        }


        // Begins an operation to accept a connection request from the client 
        //
        // <param name="acceptEventArg">The context object to use when issuing 
        // the accept operation on the server's listening socket</param>
        public void StartAccept(SocketAsyncEventArgs acceptEventArg)
        {
            if (acceptEventArg == null)
            {
                acceptEventArg = new SocketAsyncEventArgs();
                acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
            }
            else
            {
                // socket must be cleared since the context object is being reused
                acceptEventArg.AcceptSocket = null;
            }

            m_maxNumberAcceptedClients.WaitOne();
            //异步等待客户端连接  客户端连接完成后 调用acceptEventArg.Completed事件 执行 AcceptEventArg_Completed 方法
            bool willRaiseEvent = listenSocket.AcceptAsync(acceptEventArg);
            //如果 I/O 操作挂起，将返回 true。操作完成时，将引发 e 参数的 System.Net.Sockets.SocketAsyncEventArgs.Completed事件。
            //如果 I/O 操作同步完成(即调用listenSocket.AcceptAsync时立即有客户连接过来)，将返回 false。
            //将不会引发 e 参数的 System.Net.Sockets.SocketAsyncEventArgs.Completed事件
            if (!willRaiseEvent)
            {
                ProcessAccept(acceptEventArg); //经测试本行从未执行
            }
        }

        // This method is the callback method associated with Socket.AcceptAsync 
        // operations and is invoked when an accept operation is complete
        //
        void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e)
        {
            ProcessAccept(e);
        }

        private void ProcessAccept(SocketAsyncEventArgs e)
        {
            Interlocked.Increment(ref m_numConnectedSockets);
            IPEndPoint ep = (IPEndPoint)e.AcceptSocket.RemoteEndPoint;
            Console.WriteLine("A Client ({0}:{1}) connected. There are {2} clients .", ep.Address, ep.Port, m_numConnectedSockets);

            // Console.WriteLine("ClientIP:{0}:{1}", ep.Address, ep.Port);
            // Get the socket for the accepted client connection and put it into the 
            //ReadEventArg object user token
            SocketAsyncEventArgs readEventArgs = m_readWritePool.Pop();
            ((AsyncUserToken)readEventArgs.UserToken).Socket = e.AcceptSocket;

            // As soon as the client is connected, post a receive to the connection
            //异步等待从客户端接收数据 数据接收之后执行readEventArgs.Completed事件 执行IO_Completed方法
            bool willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs);
            if (!willRaiseEvent)
            {
                ProcessReceive(readEventArgs); //经测试本行从未执行
            }

            // Accept the next connection request
            //准备接受下一个连接请求
            StartAccept(e);
        }

        // This method is called whenever a receive or send operation is completed on a socket 
        //
        // <param name="e">SocketAsyncEventArg associated with the completed receive operation</param>
        void IO_Completed(object sender, SocketAsyncEventArgs e)
        {
            // determine which type of operation just completed and call the associated handler
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Receive:
                    ProcessReceive(e);
                    break;
                case SocketAsyncOperation.Send:
                    ProcessSend(e);
                    break;
                default:
                    throw new ArgumentException("The last operation completed on the socket was not a receive or send");
            }

        }

        // This method is invoked when an asynchronous receive operation completes. 
        // If the remote host closed the connection, then the socket is closed.  
        // If data was received then the data is echoed back to the client.
        //
        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            // check if the remote host closed the connection
            AsyncUserToken token = (AsyncUserToken)e.UserToken;
            if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
            {
                //increment the count of the total bytes receive by the server
                Interlocked.Add(ref m_totalBytesRead, e.BytesTransferred);
                //Console.WriteLine("The server has read a total of {0} bytes", m_totalBytesRead);

                byte[] data = new byte[e.BytesTransferred];
                Array.Copy(e.Buffer, e.Offset, data, 0, e.BytesTransferred);
                try
                {
                    ReceiveData(data, token.Socket.RemoteEndPoint);//Added by liangning 2013/8/18.

                }
                catch (Exception ex)
                {
                    lock (this)
                    {
                        Log lg = new Log();
                        lg.log(ex);//记录异常
                    }
                }
                //echo the data received back to the client
                e.SetBuffer(e.Offset, e.BytesTransferred);
               // e.SetBuffer(e.Offset, 1);
                bool willRaiseEvent = token.Socket.SendAsync(e);
                if (!willRaiseEvent)
                {
                    ProcessSend(e);
                }
                
            }
            else
            {
                CloseClientSocket(e);
            }
        }

        // This method is invoked when an asynchronous send operation completes.  
        // The method issues another receive on the socket to read any additional 
        // data sent from the client
        //
        // <param name="e"></param>
        private void ProcessSend(SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success)
            {
                // done echoing data back to the client
                AsyncUserToken token = (AsyncUserToken)e.UserToken;
                // read the next block of data send from the client
                bool willRaiseEvent = token.Socket.ReceiveAsync(e);
                if (!willRaiseEvent)
                {
                    ProcessReceive(e);
                }
            }
            else
            {
                CloseClientSocket(e);
            }
        }

        private void CloseClientSocket(SocketAsyncEventArgs e)
        {
            AsyncUserToken token = e.UserToken as AsyncUserToken;

            // close the socket associated with the client
            try
            {
                token.Socket.Shutdown(SocketShutdown.Send);
            }
            // throws if client process has already closed
            catch (Exception) { }
            token.Socket.Close();

            // decrement the counter keeping track of the total number of clients connected to the server
            Interlocked.Decrement(ref m_numConnectedSockets);
            m_maxNumberAcceptedClients.Release();
            // Console.WriteLine("A client has been disconnected from the server. There are {0} clients connected to the server", m_numConnectedSockets);
            Console.WriteLine("A client has been disconnected . There are {0} clients remaining.", m_numConnectedSockets);

            // Free the SocketAsyncEventArg so they can be reused by another client
            m_readWritePool.Push(e);
        }
        #endregion

        private void ReceiveData(Byte[] Data, EndPoint Epoint)
        {
            Byte[] HexArray = Data;
            // ProtocolPack pack = BuildData.Converting(HexArray);//安全局写法
            ProtocolPack pack = BuildData.Converting_SiChuanComm(HexArray); //四川联通写法。

            //去掉"##"结束标志
            // string dataToShow = rcvData.Substring(0, rcvData.Length - 2);
            if (ShowConsoleDataScreen)
            {
                string info = string.Format("recv data from {0} =>", Epoint.ToString());
                // Output.Print(info);
                info = string.Format("{0}", byteToHexStr(HexArray, true));
                Output.Print(info);
                info = string.Format("({0})", DateTime.Now.ToString("HH:mm:ss"));
                Output.PrintLine(info);
            }
            else
            {
                Output.Print(".");
            }

            string xml = BuildData.OutputXML(pack);
            string strHexArray = byteToHexStr(HexArray, false);
            string ip = Epoint.ToString();
            //ip = Regex.Replace(ip, ":\\d+", "");
            string SIMCode = pack.SIMCode;
            //string mac = pack.SIMCode; ;//预置mac
            DateTime dtm = DateTime.Now;
            //if (mac != "" && mac != "00-00-00-00-00-00")
            //{
            DataUti.ImportEquipData(ip, SIMCode, dtm, xml, strHexArray);
            // DataUti2.ImportEquipData(ip, mac, dtm, xml);
            //DataUti2.ImportEquipData(ip, ip, dtm, xml);
            //}
        }
        public static string byteToHexStr(byte[] bytes, bool IsShorted)
        {
            StringBuilder returnStr = new StringBuilder();
            if (!IsShorted)
            {
                if (bytes != null)
                {
                    for (int i = 0; i < bytes.Length; i++)
                    {
                        returnStr.AppendFormat(bytes[i].ToString("X2") + " ");
                    }
                }
            }
            else
            {
                if (bytes.Length > 10)
                {
                    int i;
                    for (i = 0; i < 10; i++)
                    {
                        returnStr.AppendFormat(bytes[i].ToString("X2") + " ");
                    }
                    returnStr.Append("......");
                    for (i = bytes.Length - 10; i < bytes.Length; i++)
                    {
                        returnStr.AppendFormat(bytes[i].ToString("X2") + " ");
                    }
                }
            }
            return returnStr.ToString();
        }

    }


}
